﻿/*========================== 
 * @author 郑金泉
 * @desc 方法结果类
 * ========================= */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Obsidian.Utils;
using Obsidian.Edm;

namespace Obsidian.Action
{
    public class ActionResult
    {

        private string _name;
        private string[] _fields;
        private Dictionary<string, int> _fieldIndex = new Dictionary<string, int>(
            StringComparer.OrdinalIgnoreCase);
        private List<object[]> _values = new List<object[]>();
        private string _idField;
        private string[] _modelPath = null;
        private int idFieldIndex = -1;
        private Dictionary<object, bool> dictValues = new Dictionary<object, bool>();
        private Dictionary<string, Dictionary<object, List<object[]>>> dictFieldValue
            = new Dictionary<string, Dictionary<object, List<object[]>>>(
            StringComparer.OrdinalIgnoreCase);

        private IList<ResultRelation> _listRelations = new List<ResultRelation>();
        private Dictionary<string, ResultRelation> _dictRelations = new Dictionary<string, ResultRelation>(
            StringComparer.OrdinalIgnoreCase);

        public string Name
        {
            get { return this._name; }
        }

        public string[] Fields
        {
            get { return this._fields; }
        }

        public int Count
        {
            get { return this._values.Count; }
        }

        public string IdField
        {
            get { return this._idField; }
        }

        public IList<ResultRelation> Relations
        {
            get { return this._listRelations; }
        }

        internal ActionResult(string name, string[] fields)
        {
            if (String.IsNullOrEmpty(name))
                throw new Exception("参数name不能为空");
            this._name = name;
            this.InitFields(fields);
        }

        internal ActionResult(string name, string fields)
        {
            this.Init(name, fields, null, null);
        }

        internal ActionResult(string name, string fields, string idField)
        {
            if (String.IsNullOrEmpty(idField))
                throw new Exception("参数uniqueField不能为空");

            this.Init(name, fields, idField, null);
        }

        internal ActionResult(string name, string fields, string idField, string modelPath)
        {

            if (String.IsNullOrEmpty(idField))
                throw new Exception("参数idField不能为空");

            if (String.IsNullOrEmpty(modelPath))
                throw new Exception("参数modelPath不能为空");

            this.Init(name, fields, idField, modelPath);
        }

        private void Init(string name, string fields, string idField, string modelPath)
        {
            if (String.IsNullOrEmpty(name))
                throw new Exception("参数name不能为空");

            if (String.IsNullOrEmpty(fields))
                throw new Exception("参数fields不能为空");

            if (!String.IsNullOrEmpty(name))
                this._name = name;

            this.InitFields(fields.Split(','));

            if (!String.IsNullOrEmpty(idField))
            {
                int i = 0;
                foreach (string s in this._fields)
                {
                    if (s.Equals(idField, StringComparison.OrdinalIgnoreCase))
                    {
                        idFieldIndex = i;
                        break;
                    }
                    i++;
                }
                this._idField = idField;
            }

            if (!String.IsNullOrEmpty(modelPath))
                this._modelPath = modelPath.Split(',');
        }
        private void InitFields(string[] fields)
        {
            int i = 0;
            this._fieldIndex.Clear();
            foreach (string filed in fields)
            {
                this._fieldIndex[filed] = i;
                i++;
            }
            this._fields = fields;
        }

        public void AddModels<T>(IEnumerable<T> models)
        {
            foreach (IModel m in models)
                this.AddModel(m);
        }

        public void AddModel(IModel model)
        {
            this.AddModel(model, 0);
        }

        private void AddModel(IModel model, int pathDepth)
        {
            if (_modelPath != null && pathDepth < _modelPath.Length)
            {
                string alias = _modelPath[pathDepth];
                IModelField filed = model.GetFieldByAlias(alias);
                if(filed.Type != FieldType.Entity && filed.Type != FieldType.EntityList)
                    throw new Exception(String.Format("{0}非实体类型字段", alias));
                if(filed.IsNull)
                    return;

                pathDepth++;
                if (filed.Type == FieldType.Entity)
                {
                    IModel o = (IModel)filed.OValue;
                    this.AddModel(o, pathDepth);
                }
                else if (filed.Type == FieldType.EntityList)
                {
                    IList<IModel> list = (IList<IModel>)filed.OValue;
                    foreach (IModel o in list)
                        this.AddModel(o, pathDepth);
                }
                return;
            }

            if (!String.IsNullOrEmpty(_idField))
            {
                IModelField f = model.GetFieldByAlias(_idField);
                if (f != null && !f.IsNull && !f.IsList && f.Type != FieldType.Entity)
                {
                    object val = f.OValue;
                    if (dictValues.ContainsKey(val))
                        return;
                    dictValues.Add(val, true);
                }
            }

            string[] fields = this._fields;
            object[] arrObj = new object[fields.Length];
            int i = 0;
            foreach (string fname in fields)
            {
                IModelField f = model.GetFieldByAlias(fname);
                if (f == null || f.Type == FieldType.Entity || f.Type == FieldType.EntityList)
                    arrObj[i] = null;
                else if (f.Type == FieldType.DateTime || f.Type == FieldType.Enum || f.IsList)
                    arrObj[i] = f.ToString();
                else
                    arrObj[i] = f.OValue;
                i++;
            }
            this._values.Add(arrObj);
        }

        public void AddValues(params object[] vals)
        {
            if (idFieldIndex >= 0 && idFieldIndex < vals.Length)
            {
                object val = vals[idFieldIndex];
                if (val != null)
                {
                    if (dictValues.ContainsKey(val))
                        return;
                    dictValues.Add(val, true);
                }
            }
            this._values.Add(vals);
        }

        public ActionResult SetRelation(string alias, string relationField, string aimingResult, string aimingField)
        {
            ResultRelation rr = new ResultRelation(alias, relationField, aimingResult, aimingField, ResultRelationType.Single);
            this._dictRelations[alias] = rr;
            this._listRelations.Add(rr);
            return this;
        }

        public ActionResult SetRelation(string alias, string relationField, string aimingResult, string aimingField, ResultRelationType type)
        {
            ResultRelation rr = new ResultRelation(alias, relationField, aimingResult, aimingField, type);
            this._dictRelations[alias] = rr;
            this._listRelations.Add(rr);
            return this;
        }

        public ResultRelation GetRelationByField(string alias)
        {
            ResultRelation rr;
            if (this._dictRelations.TryGetValue(alias, out rr))
                return rr;
            else
                return null;
        }

        public int GetFieldIndex(string field)
        {
            int index;
            if (this._fieldIndex.TryGetValue(field, out index))
                return index;
            else
                return -1;
        }

        public T GetModel<T>(int index) where T : IModel, new()
        {
            object[] arrObj = this._values[index];
            return this.ToModel<T>(arrObj);
        }

        public List<T> GetModelList<T>() where T : IModel, new()
        {
            List<T> mList = new List<T>();
            foreach (object[] arrObj in this._values)
            {
                T m = this.ToModel<T>(arrObj);
                mList.Add(m);
            }
            return mList;
        }

        public T GetFirstModel<T>() where T : IModel, new()
        {
            List<T> list = this.GetModelList<T>();
            if (list.Count > 0)
                return list[0];
            else
                return default(T);
        }

        public object[] GetValues(int index)
        {
            return this._values[index];
        }

        internal List<object[]> AllValues
        {
            get { return this._values; }
        }

        internal List<object[]> GetValues(string field, object value)
        {
            int fieldIndex = 0;
            foreach (string f in _fields)
            {
                if (f.Equals(field, StringComparison.OrdinalIgnoreCase))
                    break;
                fieldIndex++;
            }
            if (fieldIndex == _fields.Length)
                throw new Exception("未能找到字段" + field);

            Dictionary<object, List<object[]>> dict;
            if (!dictFieldValue.TryGetValue(field, out dict))
            {
                dict = new Dictionary<object, List<object[]>>();
                dictFieldValue[field] = dict;
            }
            List<object[]> list;
            if (!dict.TryGetValue(value, out list))
            {
                list = new List<object[]>();
                dict[value] = list;
                foreach (object[] vals in this._values)
                {
                    if (fieldIndex >= vals.Length)
                        continue;
                    object val = vals[fieldIndex];
                    if (value.Equals(val))
                        list.Add(vals);
                }
            }
            return list;

        }

        public Dictionary<string, object> GetDict(int index)
        {
            Dictionary<string, object> dict = new Dictionary<string, object>();
            object[] arrObj = this._values[index];
            string[] fields = this._fields;
            int len = fields.Length;
            int i = 0;
            foreach (string fname in fields)
            {
                dict[fname] = arrObj[i];
                i++;
                if (i == len)
                    break;
            }
            return dict;
        }

        private T ToModel<T>(object[] arrObj) where T : IModel, new()
        {
            T m = new T();
            string[] fields = this._fields;
            int len = fields.Length;
            int i = 0;
            foreach (string fname in fields)
            {
                IModelField f = m.GetFieldByAlias(fname);
                if (f != null)
                    f.Set(arrObj[i]);
                i++;
                if (i == len)
                    break;
            }
            return m;
        }

        internal void WriterOutputForCompress(JsonWriter jw)
        {
            jw.WriteStartObject();
            jw.WritePropertyName("n");
            jw.WriteValue(this.Name);
            jw.WritePropertyName("f");
            jw.WriteValue(ArrayUtil.Join(this.Fields, ","));
            jw.WritePropertyName("v");
            jw.WriteStartArray();
            foreach (object[] arrObj in this._values)
            {
                jw.WriteStartArray();
                foreach (object val in arrObj)
                    jw.WriteValue(val);
                jw.WriteEndArray();
            }
            jw.WriteEndArray();
            jw.WriteEndObject();
        }

    }
}
